テストデータ作成にCodeceptJSを使ってみた
はじめに
事業開発部でQAエンジニアをしている長友です。
今日はCodeceptJSというものを使ってみましたという記事です。
CodeceptJSはE2Eテストのフレームワークです。
今回はこちらを使ってテストデータを作成してみたというお話です。
経緯
急遽テスト実施をしてほしいということでとある案件に送り込まれました。 何もわからない状態で入ったので、どんな状況なのかやどんな感じでテストが行われているのかもわかってませんでした。
CodeceptJSについては、「Software Design 2020年6月号」〜「Software Design 2020年8月号」まで連載されていた「はじめよう、高速E2Eテスト」という記事を見たりしていて知っていました。そして、2020年の年末に開催されたソフトウェアテスト自動化カンファレンス2020で連載記事を書かれていた末村さんが「"全部乗せ" フレームワーク CodeceptJS でE2Eテストを楽にしよう」というご講演をされていたのを公開されているyoutube動画で見たりしていました。
今回の案件、何もわからないけど、たぶんテストデータとか作るのが手動だと辛そうと感じたので、ちょっとこれ使える場面があったら使ってみようと思ったのが経緯になります。
どこが便利だったのか
- プログラミングにそれほど慣れていないような私でも簡単にテストコードを作成できた。
- いろんなバリエーションのテストデータが作りやすかった。
- テストコードを動かす環境を変えてもテストコードの書き換えがいらなかった。
など。
何言ってるかわからないと思うので、次にもう少し詳しく書きます。
テストコードが書きやすかった
私のようにプログラムを書くのにそれほど慣れていない人が、CodeceptJSで書かれたテストコードを見ると、へーこんな書き方で動かせるんだと思います。
Feature('Search'); Scenario('Google検索でCodeceptJSを検索する。', ({ I }) => { I.amOnPage('https://www.google.co.jp/') I.fillField('検索', 'CodeceptJS') I.click('Google 検索') });
私と一緒に案件に加わってくださったエンジニアの方にCodeceptJSを紹介したら、日本語でコードを書いてくださいました。 上のコードを日本語で書くと、こう書けます。
Feature('Search'); Scenario('Google検索でCodeceptJSを検索する。', ({ I }) => { I.ページを移動する('https://www.google.co.jp/') I.フィールドに入力する('検索', 'CodeceptJS') I.クリックする('Google 検索') });
CodeceptJSをインストールしてinitの操作をするときに「Do you want localization for tests?」で「ja-JP」を選択しておくと、実際にこのコードが動かせて、先に書いたコードと同じ挙動をしてくれます。どんな日本語で書けるかは、作成したフォルダにある node_modules/codeceptjs/translations/ の中にある ja-JP.js というファイルをご覧ください。
(私自身は「ja-JP」を選択して、テストコード自体は英語で書いていました。)
日本語のコードを見ると、どんなテストをしようとしているかわかりやすいと思います。
- ブラウザを開いて、グーグルのサイトにアクセスする。
- 検索窓に「CodeceptJS」と入力する。
- 「Google 検索」をクリックする。
書かれている通りに動いているという感じがします。
いわゆるBDDという振る舞い駆動開発の書き方を意識して作られているそうなので、こう書けるのもうなずけます。
どうでしょうか。とても簡単だと思いませんか。
実際には、今回の案件では、最初から入ったのではなく、かなり進んだ最終段階から入ったために、いろいろ大変でした。
要素を掴むことが難しいところなどもありました。それでも、普段プログラムを書き慣れていない私のような者でもせっせとテストコードを書き続けられたのは、ユーザー操作をイメージしてテストコードが書きやすい構造になっていたからだと思います。
また、終盤の頃には、よく言われる「動く画面を見てから、お客様は本気を出す」ということで、フロントにいろいろ改修が入ってきました。しかし、それにもなんとか対応してテストコードの書き換えができていたのもこうした簡単に書けることが大きかったからです。
それと、上で書いたCodeceptJSを紹介したエンジニアの方が、データを準備しなければならない箇所で、何十回も同じような操作をする必要がありました。その場面で、CodeceptJSを使ってくださって、動いてる画面を見ながらゆっくりランチができたよとおっしゃってくれたのがうれしかったです。エンジニアの方でも普段使っていない言語だと抵抗を感じることがあると思います。それがこのCodeceptJSは簡単に書けたみたいで、敷居が低く感じてもらえるのはいいことだと思いました。
データドリブンテストの書き方の中で画面操作が書けた
なんて書いたらいいのかわからず変なこと言ってると思われたかもしれません。データの種類によって画面操作が変わるような場合もあって、そのときにデータドリブンテストの書き方が使えたと言えばいいでしょうか。
具体的にお見せできる画面がないので、架空の話になってしまいます。また、いいデータの作り方ではないかもしれません。
(詳しくは、上に書いた末村さんのソフトウェアテスト自動化カンファレンス2020の資料をご覧ください。)
こんな場面があったとします。
一人のユーザーで、
- 1つの商品を1つ買う。
- 1つの商品を2つ買う。
- 2つの商品を1つずつ買う。
- 2つの商品を2つずつ買う。
- 2つの商品を片方の商品は1つ、もう片方の商品は2つ買う。
といったデータを作りたいとします。
通常のデータドリブンテストでは、同じ買い方で、いろんな商品を買っていくなどで使われるものだと思います。
今回は上記のように同じユーザーで、いろんな買い方をするというデータが欲しかったということです。
たぶん、これはやりすぎなので、よくない例になるかもしれませんから、ご利用は控えめにしてください。
どんな書き方になるかというと以下のような感じです。
まず最初はグーグル検索の例をもとに、データドリブンテストの書き方を示します。こんな感じです。
const keywords = [ {'keyword': 'CodeceptJS'}, {'keyword': 'データドリブンテスト'}, {'keyword': 'テスト自動化'}, {'keyword': 'DevelopersIO'}, {'keyword': 'prismatix'}, ] Feature('Searchs'); Data(keywords).Scenario('Google検索でいろんなkeywordを検索する。', ({ I, current }) => { // https://www.google.co.jp/ を開く。 I.amOnPage('https://www.google.co.jp/') // 検索窓にkeywordを入力する。 I.fillField('検索', current['keyword']) // 「Google 検索」ボタンをクリックする。 I.click('Google 検索') });
キーワードとなるデータを並べておいて、それをどんどん読み込んでテストコードが実行されるという構造になっています。
次に、「データドリブンテスト」と「DevelopersIO」は「I'm Feeling Lucky」で調べたいとしたら、以下のように書きます。
const keywords = [ {'keyword': 'CodeceptJS', '検索方法': 'Google 検索' }, {'keyword': 'データドリブンテスト', '検索方法': "I'm Feeling Lucky" }, {'keyword': 'テスト自動化', '検索方法': 'Google 検索' }, {'keyword': 'DevelopersIO', '検索方法': "I'm Feeling Lucky" }, {'keyword': 'prismatix', '検索方法': 'Google 検索' }, ] Feature('Searchs2'); Data(keywords).Scenario('Google検索でいろんなkeywordを検索する。', ({ I, current }) => { // https://www.google.co.jp/ を開く。 I.amOnPage('https://www.google.co.jp/') // 検索窓にkeywordを入力する。 I.fillField('検索', current['keyword']) // 検索方法で指定された方法で検索する。 I.click(current['検索方法']) });
こう書くことで実際に検索方法が変わっていきます。 このような書き方で実現できるものを、CodeceptJSでは、データの並びの中に操作を入れ込むことができますから、その方法で書き直すと以下です。
const keywords = [ { 'keyword': 'CodeceptJS', '検索方法': (I) => { I.click('Google 検索') } }, { 'keyword': 'データドリブンテスト', '検索方法': (I) => { I.click("I'm Feeling Lucky") } }, { 'keyword': 'テスト自動化', '検索方法': (I) => { I.click('Google 検索') } }, { 'keyword': 'DevelopersIO', '検索方法': (I) => { I.click("I'm Feeling Lucky") } }, { 'keyword': 'prismatix', '検索方法': (I) => { I.click('Google 検索') } }, ] Feature('Searchs3'); Data(keywords).Scenario('Google検索でいろんなkeywordを検索する。', ({ I, current }) => { // https://www.google.co.jp/ を開く。 I.amOnPage('https://www.google.co.jp/') // 検索窓にkeywordを入力する。 I.fillField('検索', current['keyword']) // データの中に記載された方法で検索する。 current['検索方法'](I) });
ここまで示した上で、架空の買物のテストコードは以下のような感じで書けると思います。あくまで架空なので、正しいテストコードではないです。途中省略してます。
const purchases = [ { '商品1': '商品1', '数量1': '1', '購入2': (I) => {} }, { '商品1': '商品1', '数量1': '2', '購入2': (I) => {} }, { '商品1': '商品1', '数量1': '1', '購入2': (I) => { // 商品を選ぶ。 I.click('商品2') // 商品個数を指定してカートに入れる。 I.selectOption('#quantity', '1') // カートに入れる。 I.click('カートに入れる') } }, { '商品1': '商品1', '数量1': '2', '購入2': (I) => { // 商品を選ぶ。 I.click('商品2') // 商品個数を指定してカートに入れる。 I.selectOption('#quantity', '2') // カートに入れる。 I.click('カートに入れる') } }, { '商品1': '商品1', '数量1': '1', '購入2': (I) => { // 商品を選ぶ。 I.click('商品2') // 商品個数を指定してカートに入れる。 I.selectOption('#quantity', '2') // カートに入れる。 I.click('カートに入れる') } }, ] Feature('Purchases'); Data(purchases).Scenario('購入', ({ I, current }) => { // 商品を買うサイトを開く。架空のサイトです。 I.amOnPage('https://purchases.example.com/') // ログイン操作を行う。 I.fillField('メールアドレス', '[email protected]') I.fillField('パスワード', 'test1') I.click('ログイン') // 1つ目の商品を買う操作を行う。 I.click(current['商品1']) I.selectOption('#quantity', current['数量1']) I.click('カートに入れる') // 2つ目の商品を買う操作を行う。 current['購入2'](I) // 精算操作を行う。以下は書いてません。 ... });
データを作る場面で使いました。しかし、いろいろ工夫しだいで、結構複雑な画面もいろいろデータドリブンで操作ができそうな感じがしませんか。
また使える場面でいろいろ試してみようと思いました。
テストコードを実行する環境を変えたときにもそのままテストコードを使えた
これまたどんな場面と思われたかもしれません。
今回CodeceptJSを使い始めたときには、WebdriverIOを使っていました。その後、他のチームでPlaywrightを使っていていいよというお話を聞きました。 WebdriverIOのときには、最初にseleniumを起動させたりしないといけませんでした。Playwrightではそのひと手間が不要だったこともあり、変えてみました。行った内容としては、別のディレクトリを作り、そのディレクトリ内で以下のコマンドを実施します。
npm init -y npm install codeceptjs playwright --save-dev npx codeceptjs init
「npx codeceptjs init」では全部Enterキーを押しました。
もろもろ設定ファイルやテストコードなどを移しました。
変えたあともテストコードに手を加えることなく、そのまま動かせてよかったです。 上で書いた一緒にテストしてくだったエンジニアさんにも共有して、Playwrightで動かせたら、これ簡単に環境変えられていいねと言われました。
こんなケースはあまりないと思いますし、最初にどれ使うか決めてから始めると思いますから、この環境変えるのも簡単というのはどこまで皆さんの役に立つかはわからないです。
テスト実行環境を変えるのとは違う話を書いておくと、テストではブラウザを変えたりする場面があると思います。今回私が試した場面では、データ作成が一番の目的でしたので、chromeだけ使いました。
通常のテストでは、chrome以外のブラウザも使いますし、MacやWindows以外に、スマートフォンを使って試すなんてこともあると思います。そこで、MacでAppium Desktopを使い、Xcodeのsimulatorでテストするときも意外と簡単ということを書いておきます。
以下では、MacでAppium DesktopとかXcodeとかが使える状態になっていることを前提に書きます。(参考: [2020年2月版] MacでAppiumを動かしてみる)
- macOS Catalina 10.15.7
- Xcode 12.4
- simulatorで動かしたiPhoneは、iPhone 12 Pro (iOS 14.4)
- Appium Desktop 1.20.2
上記の環境で、Google検索するテストコードを試してみます。
まず、新しくディレクトリを作り、CodeceptJSが使えるようにもろもろセットアップします。 ※ appiumで動かすのでwebdriverio@6でセットアップしています。
npm init -y npm install codeceptjs webdriverio@6 --save-dev npx codeceptjs init
セットアップの中で「npx codeceptjs init」を実行する中で、「What helpers do you want to use?」というところは、「Appium」を選びます。Appiumを選ぶと「[Appium] Mobile Platform」とかが聞かれるので、今回は「iOS」を選びました。また、「[Appium] Device to run tests on (emulator)」もそのままEnterキーを押しています。 そうしてセットアップされた中にある「codecept.conf.js」というファイルを見ると、helpersの中が以下のようになっていました。
helpers: { Appium: { app: 'http://localhost', platform: 'iOS', device: 'emulator' } },
この部分を以下のように書き換えます。
helpers: { Appium: { platform: 'iOS', desiredCapabilities: { "platformName": "iOS", "platformVersion": "14.4", "deviceName": "iPhone 12 Pro", "automationName": "XCUITest", "browserName": "Safari" } } },
そしてあとは以下の手順で実行します。
- iPhone 12 Pro (iOS 14.4)のsimulatorを起動しておきます。
- Appium Destopを起動して、Hostを「127.0.0.1」にして、Portはデフォルトの「4723」のまま、「Start Server v1.20.2」ボタンをクリック。しばらくするとsimulatorのブラウザが起動して、Let's browse!と表示。
- GoogleでCodeceptJSを検索するテストコードを動かします。テストコードが「Search_test.js」というファイル名としたら、「npx codeceptjs run Search_test.js」で実行できます。「--verbose」というオプションをつけるとターミナルで動きもひとつひとつ示してくれます。
ブラウザがiPhoneのSafariになったのと、スマホでのキーボードの操作などが微妙にMac上でChromeを動かすのとは違うため、テストコードは以下です。
Feature('Search'); Scenario('Google検索でCodeceptJSを検索する。', ({ I }) => { // https://www.google.co.jp/ を開く。 I.amOnPage('https://www.google.co.jp/') // 検索窓に「CodeceptJS」と入力する。 I.fillField('検索', 'CodeceptJS') // 「Google 検索」ボタンをクリック代わりに以下の操作を行う。 I.hideDeviceKeyboard() I.pressKey('search') });
こちらを実行するとAppium Destopの画面でいろいろ動いたあと、しばらくたつとシミュレータのiPhoneでSafariが起動しはじめます。
スマホ上の操作で書き換えないといけないところはあります。しかし意外と簡単に動かせるというのが私の印象でした。
まとめ
今回CodeceptJSをE2Eテストではなく、データ作成に使ってみて、簡単にデータ作成にも使えそうだと思いました。 データ作成には、スクリプトを書いたり、APIでデータ作れるならPostmanとかを使っても簡単にできると思います。 ただ、画面をちゃんと使ってデータを作ってみたい場面もあると思いました。そうしたときには、CodeceptJSは使いやすと思いました。
今回は参加したタイミングがかなりあとの方でしたので、E2Eテストに使ってみましょうとはできませんでした。でも今後どこかの案件で早いタイミングとかで関われたら、E2Eテストに使ってみたいとも思っています。
公式サイトに、先にあげた末村さんやほかの方々のネットにあがっている情報など見ても、いろいろわかりやすい情報もたくさんあるので、ぜひ使ってみる方が増えるといいなと思いました。
私の所属する部署で開発しているprismatixでも使えそうな場面があるので、一緒にいろいろ学んでみたいという方がいたらお話してみたいとも思っています。
おまけ
CodeceptJSのちょっとおもしろい機能を使ってメール認証機能のあたりを試してみたので、おまけに載せておきます。
上記の末村さんの記事などでも紹介されているMailSlurpを使ってみるのがよいとは思います。ただ、諸般の事情で、そうしたSaaSを使えないということもあると思います。
開発環境でいろいろ試して作っているものとかの場合、なかなかSaaS使ってテストつくるとかなさそうかなと。
また、CodeceptJSの公式サイト見ていてふと目についた機能にInteractive Pauseというものがあったからこれ使えないかと思ったしだいです。
この機能は、シナリオの途中に「pause()」というものを差し込んでおいて、そこでもろもろ考えられるというものです。
自分のローカルマシンの開発環境で開発している場合、メール送信機能がまだ動いていなかったり、あとは自分のGmailには認証メールが届いていることがあります。
そこでこのpauseを使ったら、一旦そこでテストコードの実行が止まるので、その止めている間に届いたメールの認証のURLをコピーしてブラウザで開いてあげるということができます。
pauseで止まっているものを再開する場合は、exitと打ってenterキーを押せばいいです。その後、テストが再開されます。
最終的にはMailSlurpとかを使うのがよいと思います。しかし、簡単に試すだけならそうした方法を取ることもできると思いました。
以前、師と仰ぐエンジニアの方が同じように、メール認証のところだけ手動でやっているとおっしゃっていたので、それを真似ただけです。
今回はここまでです。